package com.atguigu.gmall.gateway.filter;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.util.IpUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Exchanger;
import java.util.stream.Collectors;


@Component
public class AuthGlobalFilter implements GlobalFilter {
    public static final String USER_ID_NOT_EXISTS = "userIdNotExists";
    public static final String USER_TEMPID_NOT_EXISTS = "userTempIdNotExists";
    public static final String TOKEN_STOLEN = "tokenStolen";
    private static final String URL_PATTERN_INNER = "/**/inner/**";
    private static final String URL_PATTERN_AUTH = "/**/auth/**";

    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Value("${authUrl.urlValues}")
    private String urlValues;

    @Autowired
    private RedisTemplate redisTemplate;



    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        URI uri = request.getURI();
        String requestUri = uri.toString();

        //如果匹配到路径中有inner 直接拒绝访问
        if (antPathMatcher.match(URL_PATTERN_INNER, requestUri)) {
            return asyncRefuse(exchange, ResultCodeEnum.PERMISSION);
        }

        //如果匹配到有auth 检查是否登录 如果登录且正确放行  如果没登录直接拒绝访问
        if (antPathMatcher.match(URL_PATTERN_AUTH, requestUri)) {
            String userId = loginCheck(request);
            if (USER_ID_NOT_EXISTS.equals(userId)) {
                return asyncRefuse(exchange, ResultCodeEnum.PERMISSION);
            }else{
                return allow(exchange,chain,userId);
            }
        }

        //如果匹配到有authUrl  检查是否登录 如果登录且正确放行并重定向  如果没登录重定向到登录界面
        String[] split = urlValues.split(",");
        List<String> urlList = Arrays.stream(split)
                .collect(Collectors.toList());
        int index = requestUri.lastIndexOf("/");
        String pathSuffix = requestUri.substring(index + 1);
        if (!CollectionUtils.isEmpty(urlList)) {
            if (urlList.contains(pathSuffix)) {
                String userId = loginCheck(request);
                if (USER_ID_NOT_EXISTS.equals(userId)) {
                    return syncRefuse(exchange);
                }else{
                    return allow(exchange, chain, userId);
                }
            }
        }

        //如果都不匹配 默认直接放行
        return allowAllUser(exchange, chain);

    }


    /**
     * 放行
     * @param exchange
     * @param chain
     * @param userId
     * @return
     */
    private Mono<Void> allow(ServerWebExchange exchange, GatewayFilterChain chain, String userId) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpRequest requestUserId = request.mutate().header("userId", userId).build();


        Mono<Void> filter = chain.filter(exchange.mutate().request(requestUserId).build());
        return filter;
    }

    /**
     * 如果走到最后 有临时返回值就放入消息头  没有直接返回
     * @param exchange
     * @return
     */
    private Mono<Void> allowAllUser(ServerWebExchange exchange,GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String userTempId = getUserTempId(exchange);
        if (!USER_TEMPID_NOT_EXISTS.equals(userTempId)) {
            request = request.mutate().header("userTempId", userTempId).build();
        }
        String userId = loginCheck(exchange.getRequest());
        if (!USER_ID_NOT_EXISTS.equals(userId)) {
            request = request.mutate().header("userId", userId).build();
        }
        return chain.filter(exchange.mutate().request(request).build());
    }

    /**
     * 检查是否登录
     * @param request
     * @return
     */
    private String loginCheck(ServerHttpRequest request) {
        String token = null;
        HttpHeaders headers = request.getHeaders();
        List<String> tokenList = headers.get("token");
        if (!CollectionUtils.isEmpty(tokenList)) {
            token = tokenList.get(0);
        }else{
            MultiValueMap<String, HttpCookie> cookies = request.getCookies();
            HttpCookie tokenCookie = cookies.getFirst("token");
            if (tokenCookie != null) {
                token = tokenCookie.getValue();
            }
        }
        if (!StringUtils.isEmpty(token)) {
            String redisKey = "user:login:" + token;
            String redisValue = (String) redisTemplate.opsForValue().get(redisKey);
            if (!StringUtils.isEmpty(redisValue)) {
                JSONObject jsonObject = JSONObject.parseObject(redisValue);
                String userId = jsonObject.getString("userId");
                String ip = jsonObject.getString("ip");
                if (ip != null && ip.equals(IpUtil.getGatwayIpAddress(request))) {
                    return userId;
                }
            }

        }
        return USER_ID_NOT_EXISTS;
    }

    /**
     * 获取用户临时id
     * @param exchange
     * @return
     */
    public String getUserTempId(ServerWebExchange exchange){
        String userTempId = USER_TEMPID_NOT_EXISTS;
        ServerHttpRequest request = exchange.getRequest();
        HttpHeaders headers = request.getHeaders();
        List<String> userTempIdList = headers.get("userTempId");
        if (!CollectionUtils.isEmpty(userTempIdList)) {
            userTempIdList.get(0);
        }else{
            MultiValueMap<String, HttpCookie> cookies = request.getCookies();
            HttpCookie userTempIdCookies = cookies.getFirst("userTempId");
            if (userTempIdCookies != null) {
                userTempId = userTempIdCookies.getValue();
            }
        }
        return userTempId;

    }
    /**
     * 同步拒绝访问
     * @param exchange
     * @return
     */
    private Mono<Void> syncRefuse(ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.SEE_OTHER);
        String location = "http://passport.gmall.com/login.html?originUrl=" + exchange.getRequest().getURI();
        response.getHeaders().set("location", location);
        return response.setComplete();
    }

    /**
     * 异步拒绝访问
     * @param exchange
     * @param resultCodeEnum
     * @return
     */
    private Mono<Void> asyncRefuse(ServerWebExchange exchange, ResultCodeEnum resultCodeEnum) {
        ServerHttpResponse response = exchange.getResponse();
        Result<Object> resultBuild = Result.build(null, resultCodeEnum);
        byte[] bytes = JSONObject.toJSONString(resultBuild).getBytes(StandardCharsets.UTF_8);
        DataBuffer wrap = response.bufferFactory().wrap(bytes);
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        return response.writeWith(Mono.just(wrap));
    }




}
